from rest_framework import serializers
from django.utils import timezone   # 项目中的配置时间
from decimal import Decimal
from django_redis import get_redis_connection
from django.db import transaction

from goods.models import SKU
from .models import OrderInfo, OrderGoods


# 购物车商品数据序列化器
class CartSKUSerializer(serializers.ModelSerializer):
    count = serializers.IntegerField(label='数量')

    class Meta:
        model = SKU
        fields = ('id', 'name', 'default_image_url', 'price', 'count')


# 订单结算页面的序列化器
class OrderSettlementSerializer(serializers.Serializer):
    freight = serializers.DecimalField(label='运费', max_digits=10, decimal_places=2)
    skus = CartSKUSerializer(many=True)


# 提交订单的序列化器
class CommitOrderSerializer(serializers.ModelSerializer):
    class Meta:
        model = OrderInfo
        # order_id :输出;address 和 pay_method : 输⼊
        fields = ('order_id', 'address', 'pay_method')
        read_only_fields = ('order_id',)
        # 指定address 和 pay_method 为输出
        extra_kwargs = {
            'address': {
                'write_only': True,
            },
            'pay_method': {
                'write_only': True,
            },
        }

    def create(self, validated_data):
        """创建订单记录， 保存OrderGoods和OrderInfo信息"""

        # 获取当前保存订单时需要的信息
        # 获取登录用户
        user = self.context['request'].user
        # 生成订单编号， （时间+user.id)
        # timezone.now()是datetime时间对象， 需要转成时间字符串strftime
        order_id = timezone.now().strftime('%Y%m%d%H%M%S') + ('%09d' % user.id)
        # 获取地址和支付方式
        address = validated_data.get('address')
        pay_method = validated_data.get('pay_method')
        # 明显的开启事务
        with transaction.atomic():

            # 在安全的地方创建保存点， 为了以后如果数据库操作失败可以回滚到此
            save_id = transaction.savepoint()

            try:

                # 保存订单基本信息 OrderInfo
                order = OrderInfo.objects.create(
                    order_id=order_id,
                    user=user,
                    address=address,
                    total_count=0,
                    total_amount=0,
                    freight=Decimal('10.00'),
                    pay_method=pay_method,
                    # 订单状态
                    # 如果用户选择支付宝，状态为待支付
                    # 如果用户选择货到付款， 状态为待发货
                    # 三元表达式
                    status=OrderInfo.ORDER_STATUS_ENUM['UNPAID'] if pay_method==OrderInfo.PAY_METHODS_ENUM else OrderInfo.ORDER_STATUS_ENUM['UNSEND'],
                )
                # 从redis读取购物车中所有的商品信息
                redis_conn = get_redis_connection('carts')
                redis_cart = redis_conn.hgetall('cart_%s' % user.id)
                redis_selected = redis_conn.smembers('selected_%s' % user.id)
                # 定义将来要支付的商品的信息的字典
                carts = {}
                for sku_id in redis_selected:
                    carts[int(sku_id)] = int(redis_cart[sku_id])
                # 读取出所有要支付商品的sku_id
                sku_ids = carts.keys()
                # 遍历购物车中被勾选的商品信息
                for sku_id in sku_ids:
                    # 死循环下单：库存满足， 你在下单的时候，也没有别人修改库存， 下单则成功
                    # 如果下单库存被更改， 但是你的count依然在库存范围内， 继续下单， 直到真的不满足了才下单失败
                    while True:
                        # 获取sku对象
                        sku = SKU.objects.get(id=sku_id)
                        # 获取原始的库存和销量
                        origin_count = sku.stock
                        origin_sales = sku.sales
                        #获取sku数量
                        count = carts[sku_id]
                        # 判断库存
                        if origin_count < count:
                            # 回滚
                            transaction.savepoint_rollback(save_id)
                            raise serializers.ValidationError('库存不足')

                        # # 模拟网络延迟
                        # import time
                        # time.sleep(5)


                        # # 减少库存，增加销量 SKU
                        # sku.stock -= count
                        # sku.sales += count
                        # sku.save()

                        # 获取要更新的库存和销量
                        new_count = origin_count - count
                        new_sales = origin_sales + count
                        # 使用乐观锁更改库存，解决并发问题
                        # 在调用update（）更新库存之前， 使用filter（）查询原始的库存是否存在， 如果库存不存在， update（）返回0
                        result = SKU.objects.filter(id=sku_id, stock=origin_count).update(stock=new_count, sales=new_sales)
                        if 0 == result:
                            continue

                        # 修改SPU销量
                        sku.goods.sales += count
                        sku.goods.save()
                        # 保存订单商品信息 OrderGoods
                        OrderGoods.objects.create(
                            order=order,
                            sku=sku,
                            count=count,
                            price=sku.price,
                        )
                        #  累加计算总数量和总价
                        order.total_count += count
                        order.total_amount += (count * sku.price)

                        # 下单成功跳出死循环
                        break

                # 最后加入邮费和保存订单信息
                order.total_amount += order.freight
                order.save()
                # 捕获自己抛出的异常
            except serializers.ValidationError:
                raise
            except Exception:
                transaction.savepoint_rollback(save_id)
                raise # 自动捕获异常并抛出， 不需要给异常起别名
            # 没有错误， 需要明显的提交
            transaction.savepoint_commit(save_id)
            # 清除购物车中已结算的商品
            pl = redis_conn.pipeline()
            pl.hdel('cart_%s' % user.id, *sku_ids)
            pl.srem('selected_%s' % user.id, *sku_ids)
            pl.execute()
            # 响应
            return order
